﻿using Microsoft.VisualStudio.TestTools.UnitTesting;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace NCM_MSTest
{
    /// <summary>
    /// 输入：距离为L的两个点电荷 电量为q1,q2
    /// 输出：其连线上电场强度为零的位置
    /// 建模：以q1为原点，q1q2连线的延长线为x轴建立一维坐标系
    /// 公式：E(x)=1/(4*Pi*e_0)*|q|/x^2
    /// </summary>
    internal class Example_1_2
    {
        /// <summary>
        /// 直接计算
        /// </summary>
        /// <param name="l"></param>
        /// <param name="q1"></param>
        /// <param name="q2"></param>
        /// <returns></returns>
        public static double Compute_Direct(double l, double q1, double q2)
        {
            var ret = double.NaN;
            if (Math.Abs(q1) > 0 && Math.Abs(q2) > 0)
            {
                if (q1 == -q2)
                    return double.NaN;
                if (q1 / q2 > 0)
                    ret = l / (1 + Math.Sqrt(q2 / q1));
                else
                    ret = l / (1 - Math.Sqrt(-q2 / q1));
            }
            return ret;
        }

        /// <summary>
        /// 二分法
        /// </summary>
        /// <param name="l"></param>
        /// <param name="q1"></param>
        /// <param name="q2"></param>
        /// <param name="num">最大迭代次数</param>
        /// <param name="eps">误差</param>
        /// <returns></returns>
        public static double Compute_Dichotomy(double l, double q1, double q2
            , int num = 10000
            , double eps = 1e-6
            , double perturbation = 1e-7)
        {
            if (q1 == -q2)
                return double.NaN;
            double a = 0, b = l;
            int n = 1;
            double x = (a + b) / 2;
            double r = 0d;
            do
            {
                r = Ex(l, q1, q2, x);
                if (Math.Abs(r - 0) <= eps)
                    break;
                //var r1 = Ex(l, q1, q2, x + perturbation);
                if (r > 0)
                    b = x;
                else
                    a = x;
                x = (a + b) / 2;
                n++;
            } while (n < num);
            return x;
        }

        private static double Ex(double l, double q1, double q2, double x)
        {
            if (x == 0 || x == l)
                return double.NaN;
            var epsilon0 = 1;
            var k = 4 * Math.PI * epsilon0;
            var k0 = 1 / k;
            double E0 = 0d, E1 = 0d;
            double sym0 = q1 * x > 0 ? 1 : -1;
            double sym1 = q2 * (x - l) > 0 ? 1 : -1;
            E0 = sym0 * Math.Abs(q1) / Math.Pow(x, 2);
            E1 = sym1 * Math.Abs(q2) / Math.Pow(x - l, 2);
            return k0 * (E0 + E1);
        }
    }

    [TestClass]
    [TestCategory("1.2 计算电场强度为零的位置")]
    public class Example_1_2_Test
    {
        [TestMethod("直接法")]
        [DataRow(4.0, -1d, -2d, 1.656854d)]
        //[DataRow(4.0, 1d, 2d, 1.656854d)]
        public void Compute(double l, double q1, double q2, double val)
        {
            var r0 = Example_1_2.Compute_Direct(l, q1, q2);
            var r1 = Example_1_2.Compute_Dichotomy(l, q1, q2);
            Assert.IsTrue(r0.EqualsDelta(val));
            Assert.IsTrue(r1.EqualsDelta(val));
        }
    }

    public static class NumberEquals
    {
        public static bool EqualsDelta(this double a, double b, double tolerance = 1e-5)
        {
            if (tolerance < 0)
                tolerance = -tolerance;
            return Math.Abs(a - b) < tolerance;
        }
    }
}
